<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="VHHZZ" id="VHHZZ"><span data-lake-id="uf32334bb" id="uf32334bb">典型回答</span></h1>
  <p data-lake-id="u489a323b" id="u489a323b"><br></p>
  <p data-lake-id="ue941a949" id="ue941a949"><span data-lake-id="u95e1742b" id="u95e1742b">缓存预热是在系统启动或者在业务高峰期之前，将一些数据加载到缓存中，以提高系统的性能和响应速度。</span></p>
  <p data-lake-id="u4b3882ee" id="u4b3882ee"><span data-lake-id="u69ac343e" id="u69ac343e">​</span><br></p>
  <p data-lake-id="u1293f648" id="u1293f648"><span data-lake-id="ue94d2400" id="ue94d2400">一般来说，我们在进行缓存预热的时候，有以下几种方案：</span></p>
  <p data-lake-id="u4d4dc396" id="u4d4dc396"><span data-lake-id="u687a695e" id="u687a695e">​</span><br></p>
  <h3 data-lake-id="CvIBR" id="CvIBR"><span data-lake-id="u62ed6664" id="u62ed6664">启动过程中预热</span></h3>
  <p data-lake-id="u31525692" id="u31525692"><br></p>
  <p data-lake-id="ua1ff7700" id="ua1ff7700"><span data-lake-id="udd53a335" id="udd53a335">比较常见的一种实现方式就是在系统启动的时候预热，这种方式一般对于本地缓存十分常见，因为本地缓存本身就是基于JVM内存的，当应用重启时，本地缓存就会被清空，而在应用启动之后，就需要把本地缓存的内容预热好。这样才能起到缓存的效果。</span></p>
  <p data-lake-id="u82a0a631" id="u82a0a631"><span data-lake-id="ue417391e" id="ue417391e">​</span><br></p>
  <p data-lake-id="u68e468f3" id="u68e468f3"><span data-lake-id="ude3af57e" id="ude3af57e">那么，一种有效的方式就是基于Spring来实现。在Spring应用程序启动时，可以通过监听应用启动事件，或者在应用的初始化阶段，将需要缓存的数据加载到缓存中。这可以通过在启动时调用相关服务或者数据访问层的方法来实现。</span></p>
  <p data-lake-id="u62570c34" id="u62570c34"><span data-lake-id="u29745083" id="u29745083">​</span><br></p>
  <p data-lake-id="uc9f44d08" id="uc9f44d08"><span data-lake-id="u518df38a" id="u518df38a">Spring其实提供了一系列事件及扩展点，可以让我们在他的生命周期的各个阶段做我们想做的事情。如基于ApplicationReadyEvent、CommandLineRunner、InitializingBean、@PostConstruct等。</span></p>
  <p data-lake-id="u5062ea92" id="u5062ea92"><span data-lake-id="u5b80bda7" id="u5b80bda7">​</span><br></p>
  <p data-lake-id="u0e6b05d5" id="u0e6b05d5"><span data-lake-id="u8ae26f8f" id="u8ae26f8f">​</span><br></p>
  <h3 data-lake-id="bDm42" id="bDm42"><strong><span data-lake-id="uc493bf96" id="uc493bf96" class="lake-fontsize-12" style="color: var(--tw-prose-bold)">定时任务</span></strong></h3>
  <p data-lake-id="ud355d4ff" id="ud355d4ff"><br></p>
  <p data-lake-id="u8e1ec414" id="u8e1ec414"><span data-lake-id="ue02b481a" id="ue02b481a">在启动过程中预热有一个问题，那就是一旦启动之后，如果需要预热新的数据，或者需要修改数据，就不支持了，那么，在应用的运行过程中，我们也是可以通过定时任务来实现缓存的更新预热的。</span></p>
  <p data-lake-id="u7ef8be74" id="u7ef8be74"><br></p>
  <p data-lake-id="u5023e4f5" id="u5023e4f5"><span data-lake-id="udfb9733f" id="udfb9733f" style="color: rgb(15, 15, 15)">我们通常依赖这种方式来确保缓存中的数据是最新的，避免因为业务数据的变化而导致缓存数据过时。</span></p>
  <p data-lake-id="u6f2344a0" id="u6f2344a0"><span data-lake-id="u0ac915c3" id="u0ac915c3" style="color: rgb(15, 15, 15)">​</span><br></p>
  <p data-lake-id="u102565b8" id="u102565b8"><span data-lake-id="u758157d8" id="u758157d8" style="color: rgb(15, 15, 15)">在Spring中，想要实现一个定时任务也挺简单的，基于@Scheduled就可以轻易实现</span></p>
  <p data-lake-id="ueeec8ca8" id="ueeec8ca8"><span data-lake-id="u24e3f9f9" id="u24e3f9f9" style="color: rgb(15, 15, 15)">​</span><br></p>
  <pre lang="java"><code>
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
public void scheduledCachePreload() {
    // 执行缓存预热逻辑
    // ...
}
</code></pre>
  <p data-lake-id="u26b4b98f" id="u26b4b98f"><br></p>
  <p data-lake-id="uda526fc2" id="uda526fc2"><span data-lake-id="u3c08d636" id="u3c08d636">​</span><br></p>
  <h3 data-lake-id="LVP9S" id="LVP9S"><span data-lake-id="u1e9f9aaf" id="u1e9f9aaf" style="color: var(--tw-prose-bold)">用时加载</span></h3>
  <p data-lake-id="uaa0c0e1d" id="uaa0c0e1d"><br></p>
  <p data-lake-id="u57e26e66" id="u57e26e66"><span data-lake-id="uedd4942e" id="uedd4942e">就像Redis可以基于用户的查询来进行延迟删除key一样 ，我们也可以基于用户的查询进行延迟的更新缓存。</span></p>
  <p data-lake-id="u83c91435" id="u83c91435"><span data-lake-id="u34f38de1" id="u34f38de1" class="lake-fontsize-12" style="color: rgb(15, 15, 15)">​</span><br></p>
  <p data-lake-id="ub209404e" id="ub209404e"><span data-lake-id="uc3815729" id="uc3815729" class="lake-fontsize-12" style="color: rgb(15, 15, 15)">也就是说，在用户请求到来时，根据用户的访问模式和业务需求，动态地将数据加载到缓存中。这种方式更加灵活，可以根据实际需求选择性地预热缓存。</span></p>
  <p data-lake-id="udc7fa7b2" id="udc7fa7b2"><span data-lake-id="u7038f72d" id="u7038f72d" class="lake-fontsize-12" style="color: rgb(15, 15, 15)">​</span><br></p>
  <pre lang="java"><code>
public Data fetchData(String key) {
    // 先检查缓存中是否存在数据
    Data cachedData = cache.get(key);

    if (cachedData == null) {
        // 如果缓存中不存在，根据业务需求加载数据到缓存中
        // ...
    }

    return cachedData;
}

</code></pre>
  <p data-lake-id="u9c1b539a" id="u9c1b539a"><br></p>
  <h3 data-lake-id="sobEc" id="sobEc"><span data-lake-id="u32006e53" id="u32006e53">缓存加载器</span></h3>
  <p data-lake-id="u0bf802c6" id="u0bf802c6"><br></p>
  <p data-lake-id="u79fd95ec" id="u79fd95ec"><span data-lake-id="u5893fc1f" id="u5893fc1f">一些缓存框架提供了缓存加载器的机制，可以在缓存中不存在数据时，自动调用加载器加载数据到缓存中。这样可以简化缓存预热的逻辑。如Caffeine中就有这样的功能：</span></p>
  <p data-lake-id="uc7ef9f04" id="uc7ef9f04"><span data-lake-id="ua9561d60" id="ua9561d60">​</span><br></p>
  <pre lang="java"><code>
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class MyCacheService {

    private final LoadingCache&lt;String, String&gt; cache;

    public MyCacheService() {
        this.cache = Caffeine.newBuilder()
                .refreshAfterWrite(1, TimeUnit.MINUTES)  // 配置自动刷新，1分钟刷新一次
                .build(key -&gt; loadDataFromSource(key));  // 使用加载器加载数据
    }

    public String getValue(String key) {
        return cache.get(key);
    }

    private String loadDataFromSource(String key) {
        // 从数据源加载数据的逻辑
        // 这里只是一个示例，实际应用中可能是从数据库、外部服务等获取数据
        System.out.println("Loading data for key: " + key);
        return "Value for " + key;
    }
}

</code></pre>
  <p data-lake-id="u78e21934" id="u78e21934"><span data-lake-id="u4a011068" id="u4a011068">​</span><br></p>
  <p data-lake-id="u3e358017" id="u3e358017"><span data-lake-id="ueadbd0b0" id="ueadbd0b0">在上面的例子中，我们使用 </span><span data-lake-id="uf8d5f595" id="uf8d5f595">Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES)</span><span data-lake-id="ufbbaebb5" id="ufbbaebb5"> 配置了缓存的自动刷新机制，即每个缓存项在写入后的1分钟内，如果有读请求，Caffeine 会自动触发数据的刷新。</span></p>
  <p data-lake-id="u8d3d2677" id="u8d3d2677"><span data-lake-id="u82b5334a" id="u82b5334a">loadDataFromSource 方法是用于加载数据的自定义方法。你可以在这个方法中实现从数据源（例如数据库、外部服务）加载数据的逻辑。</span></p>
 </body>
</html>